/*
**NCS list Library Procedures
**This module provide the list API for NCS 
**
**
**Copyright (C) 2016-xxxx andisat <www.andisat.com>
**
**Author: andisat
**Date:2016-1-10
**
*/
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include "ncs_list.h"

/* Allocate new list. */
struct list *
list_new (int (*cmp)(void *val1,void *val2),
        void (*del)(void *data))
{
    struct list *new;

    new = (struct list*)malloc (sizeof (struct list));
    memset (new, 0, sizeof (struct list));
    new->cmp=cmp;
    new->del=del;
    
    return new;
}

/* Free list. */
void
list_free (struct list *l)
{
    if(l)
        free (l);
}

/* Allocate new listnode.  Internal use only. */
static struct listnode *
listnode_new ()
{
    struct listnode *node;

    node = (struct listnode *)malloc(sizeof (struct listnode));
    memset (node, 0, sizeof (struct listnode));
    return node;
}

/* Free listnode. */
static void
listnode_free (struct listnode *node)
{
    if(node)
        free(node);
}

/* Add new data to the list. */
void
listnode_add (struct list *list, void *val)
{
  struct listnode *node;

  node = listnode_new ();

  node->prev = list->tail;
  node->data = val;

  if (list->head == NULL)
    list->head = node;
  else
    list->tail->next = node;
  list->tail = node;

  list->count++;
}

/* Add new node with sort function. */
void
listnode_add_sort (struct list *list, void *val)
{
    struct listnode *n;
    struct listnode *new;

    new = listnode_new ();
    new->data = val;

    if (list->cmp)
    {
        for (n = list->head; n; n = n->next)
	{
	  if ((*list->cmp) (val, getdata(n)) < 0)
	    {	    
	      new->next = n;
	      new->prev = n->prev;

	      if (n->prev)
		n->prev->next = new;
	      else
		list->head = new;
	      n->prev = new;
	      list->count++;
	      return;
	    }
	}
    }

  new->prev = list->tail;

  if (list->tail)
    list->tail->next = new;
  else
    list->head = new;

  list->tail = new;
  list->count++;
}

void
listnode_add_after (struct list *list, struct listnode *pp, void *val)
{
    struct listnode *nn;

    nn = listnode_new ();
    nn->data = val;

    if (pp == NULL)
    {
        if (list->head)
    	    list->head->prev = nn;
        else
	    list->tail = nn;

        nn->next = list->head;
        nn->prev = pp;

        list->head = nn;
    }
    else
    {
        if (pp->next)
            pp->next->prev = nn;
        else
            list->tail = nn;

        nn->next = pp->next;
        nn->prev = pp;

        pp->next = nn;
    }
}


/* Delete specific date pointer from the list. */
void
listnode_delete (struct list *list, void *val)
{
    struct listnode *node;

    if(list->cmp)
        for (node = list->head; node; node = node->next)
        {
            if (list->cmp(val,getdata(node)))
            {
                if (node->prev)
                    node->prev->next = node->next;
                else
                    list->head = node->next;

                if (node->next)
                    node->next->prev = node->prev;
                else
                    list->tail = node->prev;

                list->count--;
                listnode_free (node);
                return;
            }
        }
}

/* Return first node's data if it is there.  */
void *
listnode_head (struct list *list)
{
    struct listnode *node;

    node = list->head;

    if (node)
        return node->data;
    return NULL;
}

/* Delete all listnode from the list. */
void
list_delete_all_node (struct list *list)
{
    struct listnode *node;
    struct listnode *next;

    for (node = list->head; node; node = next)
    {
        next = node->next;
        if (list->del)
            (*list->del) (node->data);
        listnode_free (node);
    }
    list->head = list->tail = NULL;
    list->count = 0;
}

/* Delete all listnode then free list itself. */
void
list_delete (struct list *list)
{
    struct listnode *node;
    struct listnode *next;

    for (node = list->head; node; node = next)
    {
        next = node->next;
        if (list->del)
            (*list->del) (node->data);
        listnode_free (node);
    }
    list_free (list);
}

/* Lookup the node which has given data. */
struct listnode *
listnode_lookup (struct list *list, void *val)
{
    listnode node;

    if(list->cmp)
        for (node = list->head; node; nextnode (node))
            if(!list->cmp(val,getdata (node)))
                return node;
        
    return NULL;
}

void
list_delete_node (list list, listnode node)
{
    if (node->prev)
        node->prev->next = node->next;
    else
        list->head = node->next;
    if (node->next)
        node->next->prev = node->prev;
    else
        list->tail = node->prev;
    list->count--;
    listnode_free (node);
}


void
list_add_node_prev (list list, listnode current, void *val)
{
    struct listnode *node;

    node = listnode_new ();
    node->next = current;
    node->data = val;

    if (current->prev == NULL)
        list->head = node;
    else
        current->prev->next = node;

    node->prev = current->prev;
    current->prev = node;

    list->count++;
}


void
list_add_node_next (list list, listnode current, void *val)
{
    struct listnode *node;

    node = listnode_new ();
    node->prev = current;
    node->data = val;

    if (current->next == NULL)
        list->tail = node;
    else
        current->next->prev = node;

    node->next = current->next;
    current->next = node;

    list->count++;
}

